/** * \package com.bluevia.messagery.mo.mms This package contains the classes in order to receive MMS using Bluevia API. * \package com.bluevia.messagery.mo.mms.client This package contains REST client to receive MMS using Bluevia API. */ package com.bluevia.messagery.mo.mms.client; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.mail.MessagingException; import javax.mail.internet.MimeMultipart; import javax.mail.util.ByteArrayDataSource; import org.apache.log4j.Logger; import com.bluevia.commons.Encoding; import com.bluevia.commons.Entity; import com.bluevia.commons.GsdpConstants; import com.bluevia.commons.Utils; import com.bluevia.commons.connector.GenericResponse; import com.bluevia.commons.data.JaxbEntity; import com.bluevia.commons.data.UserId; import com.bluevia.commons.data.UserId.Type; import com.bluevia.commons.exception.BlueviaException; import com.bluevia.commons.exception.ConnectorException; import com.bluevia.commons.parser.ParseException; import com.bluevia.commons.parser.xml.XmlConstants; import com.bluevia.commons.parser.xml.XmlParser; import com.bluevia.messagery.mo.client.BVMoClient; import com.bluevia.messagery.mo.data.ReceivedMessageList; import com.bluevia.messagery.mo.mms.data.MimeContent; import com.bluevia.messagery.mo.mms.data.MmsMessage; import com.bluevia.messagery.mo.mms.data.MmsMessageInfo; import com.bluevia.messagery.mo.mms.parser.MultipartMmsParser; import com.telefonica.schemas.unica.rest.common.v1.SimpleReferenceType; import com.telefonica.schemas.unica.rest.common.v1.UserIdType; import com.telefonica.schemas.unica.rest.mms.v1.MessageNotificationType; import com.telefonica.schemas.unica.rest.mms.v1.ReceivedMessagesType; /** * * Abstract client interface for the REST binding of the Bluevia MMS MO Service. * * @author Telefonica R&D * */ public abstract class BVMoMmsClient extends BVMoClient { private static Logger log = Logger.getLogger(BVMoMmsClient.class.getName()); private static final String ATTACHMENTS_FEED_PATH = "/attachments"; /** * Initializer of common attributes */ protected void init(){ mEncoding = Encoding.APPLICATION_XML; mParser = new XmlParser(); mSerializer = null; //No serializer } /** * * Retrieves the list of received messages without attachment ids. * * @see getAllMessages(String registrationId, boolean attachUrl) * * Note: the origin address of the received MMS will contain an alias, not a phone number. * * @param registrationId the registration id (short number) that receives the messages * @return the list of messages. * @throws BlueviaException * @throws IOException */ public ArrayList<MmsMessageInfo> getAllMessages(String registrationId) throws BlueviaException, IOException{ return getAllMessages(registrationId, false); } /** * Retrieves the list of received messages. Depending on the value of the useAttachmentsIds parameter, the response will * include the IDs of the attachments or not. * If the ids are retrieved, the function 'getAttachment' can be used; otherwise, the attachments must be obtained throught the getMessage function. * * @see getMessage(String registrationId, String messageId) * @see getAttachment(String registrationId, String messageId, String attachmentId) * * Note: the origin address of the received MMS will contain an alias, not a phone number. * * @param registrationId the registration id (short number) that receives the messages * @param attachUrl the boolean parameter to retrieve the IDs of the attachments or not * @return the list of Received MMSs (list will be empty if the are no messages) * @throws BlueviaException * @throws IOException */ public ArrayList<MmsMessageInfo> getAllMessages(String registrationId, boolean attachUrl) throws BlueviaException, IOException{ ArrayList<MmsMessageInfo> receivedMmsList = null; // Check params checkRegistrationId(registrationId); // Build feed uri for the request String feedUri = "/" + registrationId + RECEIVED_MESSAGES; HashMap<String, String> parameters = new HashMap<String, String>(); if (attachUrl) parameters.put(XmlConstants.PARAM_USE_ATTACHMENT_URLS, "true"); parameters.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1); ((XmlParser) mParser).setParseClass(ReceivedMessagesType.class); Entity response = retrieve(feedUri, parameters); receivedMmsList = getReceivedMmsListFromResponse(response); return receivedMmsList; } /** * Gets the received MMS list from the response Entity * * @param response response Entity that contains the result * @return the received MMS list * @throws BlueviaException if the response or the list are not valid. */ private ArrayList<MmsMessageInfo> getReceivedMmsListFromResponse(Entity response) throws BlueviaException{ ArrayList<MmsMessageInfo> receivedMmsList = null; //If there are no messages, return empty list if (response == null) return new ArrayList<MmsMessageInfo>(); //Check if response is instance of JaxbEntity else if (! (response instanceof JaxbEntity)) throw new BlueviaException("Error during request. Response received does not correspond to an ReceivedMmsList", BlueviaException.INTERNAL_CLIENT_ERROR); JaxbEntity parseEntity= (JaxbEntity) response; //Check if parseEntity is instance of ReceivedMessagesType ReceivedMessagesType receivedMessagesType= new ReceivedMessagesType(); if ((parseEntity.getObject() == null) || (! (parseEntity.getObject() instanceof ReceivedMessagesType))) throw new BlueviaException("Error during request. Response received does not correspond to an MessageReferenceType", BlueviaException.INTERNAL_CLIENT_ERROR); receivedMessagesType = (ReceivedMessagesType) parseEntity.getObject(); log.debug("Creating response ReceivedMessageList"); Entity retEntity = new ReceivedMessageList(receivedMessagesType); //Check if response is instance of DeliveryInfo if ((retEntity == null) || (! (retEntity instanceof ReceivedMessageList))) throw new BlueviaException("Error during request. Response received does not correspond to an ReceivedMessageList", BlueviaException.INTERNAL_CLIENT_ERROR); //Get received data receivedMmsList = ((ReceivedMessageList) retEntity).getList(); // Check list if (receivedMmsList == null) throw new BlueviaException("ReceivedMessage list is null", BlueviaException.INTERNAL_CLIENT_ERROR); // Check list if (receivedMmsList == null) throw new BlueviaException(ReceivedMessageList.class.getName() + " object is null", BlueviaException.INTERNAL_CLIENT_ERROR); return receivedMmsList; } /** * Gets the content of a message with a 'messageId' sent to the 'registrationId' * * @param registrationId the registration id (short number) that receives the messages * @param messageId the message id (obtained in getAllMessages function) * @return the MmsMessage the complete MmsMessage (including attachments) * @throws BlueviaException * @throws IOException */ public MmsMessage getMessage(String registrationId, String messageId) throws BlueviaException, IOException{ // Check params checkRegistrationId(registrationId); if (Utils.isEmpty(messageId)) throw new BlueviaException("Bad request: Message identifier is either null or empty", BlueviaException.BAD_REQUEST_EXCEPTION); // Build feed uri for the request String feedUri = "/" + registrationId + RECEIVED_MESSAGES + "/" + messageId; HashMap<String, String> parameters = new HashMap<String, String>(); parameters.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1); ((XmlParser) mParser).setParseClass(ReceivedMessagesType.class); Entity response = retrieve(feedUri, parameters); if (response == null || !(response instanceof MmsMessage)) throw new BlueviaException("Error during request. Response received does not correspond to an " + MmsMessage.class.getName(), BlueviaException.INTERNAL_CLIENT_ERROR); return (MmsMessage) response; } /** * * Gets the attachment with the specified id of the received message. * * @param registrationId the registration id (short number) that receives the messages * @param messageId the message id (obtained in getAllMessages function) * @param attachmentId the attachment id (obtained in getAllMessages function) * @return the attachment of the received MMS. * @throws BlueviaException * @throws IOException */ public MimeContent getAttachment(String registrationId, String messageId, String attachmentId) throws BlueviaException, IOException { // Check params checkRegistrationId(registrationId); if (Utils.isEmpty(messageId)) throw new BlueviaException("Bad request: Message identifier is either null or empty", BlueviaException.BAD_REQUEST_EXCEPTION); if (Utils.isEmpty(attachmentId)) throw new BlueviaException("Bad request: Attachment identifier is either null or empty", BlueviaException.BAD_REQUEST_EXCEPTION); // Build feed uri for the request String feedUri = "/" + registrationId + RECEIVED_MESSAGES + "/" + messageId + ATTACHMENTS_FEED_PATH + "/" + attachmentId; HashMap<String, String> parameters = new HashMap<String, String>(); parameters.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1); ((XmlParser) mParser).setParseClass(ReceivedMessagesType.class); Entity response = this.retrieve(feedUri, parameters); if (response == null || !(response instanceof MimeContent)) throw new BlueviaException("Error during request. Response received does not correspond to a " + MimeContent.class.getName(), BlueviaException.INTERNAL_CLIENT_ERROR); return (MimeContent) response; } @Override protected Entity retrieve(String feedUri, HashMap<String, String> parameters) throws IOException, BlueviaException { if (feedUri.endsWith(RECEIVED_MESSAGES)){ return super.retrieve(feedUri, parameters); } else { String uri = mBaseUri + feedUri; GenericResponse response = mConnector.retrieve(uri, parameters); InputStream is = response.getAdditionalData().getBody(); HashMap<String, String> responseHeaders = response.getAdditionalData().getHeaders(); try { String contentType = responseHeaders.get("Content-Type"); String contentDisp = responseHeaders.get("Content-Disposition"); MultipartMmsParser parser = new MultipartMmsParser(mParser); if (feedUri.contains(ATTACHMENTS_FEED_PATH)){ if (contentType.contains("xml") || contentType.contains("smil") || contentType.contains("text")) return (Entity) parser.buildMimeContent(contentType, contentDisp, Utils.convertStreamToString(is), true); else return (Entity) parser.buildMimeContent(contentType, contentDisp, is, true); } else { ByteArrayDataSource ds = new ByteArrayDataSource(is, contentType); MimeMultipart multipart = new MimeMultipart(ds); return (Entity) parser.parseMultipart(multipart); } } catch (MessagingException e) { throw new ParseException("Error parsing multipart: " + e.getLocalizedMessage(), e); } finally { if (is != null) is.close(); } } } /** * Subscribe notifications * * @param phoneNumber the phoneNumber * @param endpoint the uri to receive notifications * @param criteria field with keyword with criteria notifications * @return the correlator from the response. * @throws BlueviaException if the response or the result are not valid. * @throws IOException */ public String startNotification (String phoneNumber, String endpoint, String criteria) throws IOException, BlueviaException{ return startNotification(phoneNumber, endpoint, criteria, null); } /** * Subscribe notifications * * @param phoneNumber the phoneNumber * @param endpoint the uri to receive notifications * @param criteria field with keyword with criteria notifications * @param correlator field with correlator to identify subscriptions * @return the correlator from the response. * @throws BlueviaException if the response or the result are not valid. * @throws IOException */ public String startNotification (String phoneNumber, String endpoint, String criteria, String correlator) throws IOException, BlueviaException{ MessageNotificationType notification= new MessageNotificationType(); if (Utils.isEmpty(correlator)) { correlator= generateCorrelator(criteria); } //Endpoint and correlator SimpleReferenceType ref = new SimpleReferenceType(); ref.setEndpoint(endpoint); ref.setCorrelator(correlator); notification.setReference(ref); //Addresses UserId userId= new UserId(Type.PHONE_NUMBER, phoneNumber); List<UserIdType> addresses = new ArrayList<UserIdType>(); addresses.add(userId.toUserIdType()); notification.setDestinationAddress(addresses); //Criteria notification.setCriteria(criteria); return startNotification(notification); } /** * Subscribe notifications * * @param notification the messageNotificationType with all data to subscribe * @return the correlator from the response. * @throws BlueviaException if the response or the result are not valid. * @throws IOException */ protected String startNotification (MessageNotificationType notification) throws IOException, BlueviaException{ String result = null; GenericResponse response = null; InputStream is = null; try { // Build feed uri for the request String feedUri = SUBSCRIPTION_PATH; HashMap<String, String> params = new HashMap<String, String>(); params.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1); JaxbEntity messageEntity= new JaxbEntity(); messageEntity.setJcInstance(XmlConstants.XSD_MMS_API_INSTANCE); messageEntity.setNamespace(XmlConstants.NS_MMS_API_URI); messageEntity.setQname(XmlConstants.XSD_MMSNOTIFICATION_MMSMESSAGE); messageEntity.setObject(notification); byte[] bytes = serializeEntity(messageEntity); String uri = mBaseUri + feedUri; HashMap<String, String> headers = new HashMap<String, String>(); headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding)); response = mConnector.create(uri, params, bytes, headers); is = response.getAdditionalData().getBody(); result = getMesageIdFromResponse(response.getAdditionalData().getHeaders(), uri); } catch (ConnectorException e){ log.error("Error during request. Connector Exception: " + e.getMessage()); throw new BlueviaException("Error during request: " + e.getMessage(), BlueviaException.INTERNAL_CLIENT_ERROR); } catch (IOException e) { log.error("Error during request. IO Exception: " + e.getMessage()); throw new IOException("Error during request: " + e.getMessage()); } finally { //Close the stream if (is != null) try { is.close(); } catch (IOException e) { log.info(e.getMessage(), e); } } return result; } /** * Unsubscribe notifications * * @param correlator the correlator to unsubscribe notification * @return boolean with result of operation * @throws BlueviaException if the response or the result are not valid. * @throws IOException */ public boolean stopNotification (String correlator) throws BlueviaException, IOException { boolean result = false; if (Utils.isEmpty(correlator)) throw new BlueviaException("Invalid parameter: correlator can't be empty", BlueviaException.INTERNAL_CLIENT_ERROR); GenericResponse response = null; InputStream is = null; try { // Build feed uri for the request String feedUri = SUBSCRIPTION_PATH + "/" + correlator; HashMap<String, String> params = new HashMap<String, String>(); params.put(XmlConstants.PARAM_VERSION_KEY, XmlConstants.VERSION_1); String uri = mBaseUri + feedUri; HashMap<String, String> headers = new HashMap<String, String>(); headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding)); response = mConnector.delete(uri, params, headers); log.debug("[stopNotification] response status :" + response.getStatus()); log.debug("[stopNotification] response message:" + response.getMessage()); if (response.getStatus()== 204) { result= true; } } catch (ConnectorException e){ log.error("Error during request. Connector Exception: " + e.getMessage()); throw new BlueviaException("Error during request: " + e.getMessage(), BlueviaException.INTERNAL_CLIENT_ERROR); } catch (IOException e) { log.error("Error during request. IO Exception: " + e.getMessage()); throw new IOException("Error during request: " + e.getMessage()); } finally { //Close the stream if (is != null) try { is.close(); } catch (IOException e) { log.info(e.getMessage(), e); } } return result; } }